#include "EventTable.h"

#include "EventFileDriver.h"
#include "EventHash.h"
#include "EventList.h"
#include "EventNode.h"
#include "EventString.h"

#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define _(String) gettext(String)

static void
event_item_finalize(EventItem *self)
{
	event_object_unref(self->event_describe);
	event_object_unref(self->event_name);
}

static EventItem *
event_item_new(void)
{
	EventItem *object = MALLOC_NEW(EventItem);

	event_object_init(EVENT_OBJECT(object));
	event_object_set_finalize(object, FINALIZE(event_item_finalize));

	object->event_id       = 0;
	object->create_time    = 0L;
	object->create_uid     = 0;
	object->end_time       = 0L;
	object->modify_time    = 0L;
	object->event_describe = NULL;
	object->event_name     = NULL;
	object->is_important   = false;

	return object;
}

struct _EventTable
{
	EventFileDriver parent_instance;

	/* EventTable 对应的事件组 GroupID */
	// euint32 group_id;

	/* 事件条目表本体，根据 EventID 查询 */
	EventHash *e_table;
};

static int
event_table_parser(EventFileDriver *driver, const epointer data, int length)
{
	EventList *list, *node;
	EventItem *item;

	return_val_if_fail(driver != NULL, event_table_parser, 1);

	list = event_list_new_with_raw(data);
	if (!list)
		return 1;
	node		  = list;
	item		  = event_item_new();
	item->event_id	  = (euint32)EN_GET_D(node, euint64);
	node		  = EN_GET_NEXT(node, EventList *);
	item->create_uid  = (euint32)EN_GET_D(node, euint64);
	node		  = EN_GET_NEXT(node, EventList *);
	item->create_time = EN_GET_D(node, eint64);
	node		  = EN_GET_NEXT(node, EventList *);
	item->modify_time = EN_GET_D(node, eint64);
	node		  = EN_GET_NEXT(node, EventList *);
	item->end_time	  = EN_GET_D(node, eint64);
	node		  = EN_GET_NEXT(node, EventList *);
	item->event_name  = EO_REF(EN_GET_D(node, EventString *), EventString);
	node		  = EN_GET_NEXT(node, EventList *);
	item->event_describe =
		EO_REF(EN_GET_D(node, EventString *), EventString);
	node		   = EN_GET_NEXT(node, EventList *);
	item->is_important = (bool)EN_GET_D(node, euint64);
	/* 将事件条目添加到事件表中 */
	event_hash_set(EVENT_TABLE(driver)->e_table,
		       EPTR((euint64)item->event_id), item, event_object_unref);
	event_object_unref(list);
	return 0;
}

static EventFileDriverPack *
event_table_writer(EventFileDriver *driver)
{
	EventTable *self = EVENT_TABLE(driver);
	EventItem  *e_item;
	EventList  *list;
	EventRaw   *raw;
	HashItem_p  h_item;

	return_val_if_fail(driver != NULL, event_table_writer, NULL);
	h_item = event_hash_iter_get(self->e_table);
	if (!h_item)
		return NULL;
	e_item = (EventItem *)event_hash_item_get_value(h_item);
	list   = event_list_new();
	event_list_add_data(list, EPTR((euint64)e_item->event_id),
			    EVENT_TYPE_UINT32, NULL);
	event_list_add_data(list, EPTR((euint64)e_item->create_uid),
			    EVENT_TYPE_UINT32, NULL);
	event_list_add_data(list, EPTR(e_item->create_time), EVENT_TYPE_INT64,
			    NULL);
	event_list_add_data(list, EPTR(e_item->modify_time), EVENT_TYPE_INT64,
			    NULL);
	event_list_add_data(list, EPTR(e_item->end_time), EVENT_TYPE_INT64,
			    NULL);
	event_list_add_data(list, EO_REF(EPTR(e_item->event_name), EventString),
			    EVENT_TYPE_STRING, event_object_unref);
	event_list_add_data(list,
			    EO_REF(EPTR(e_item->event_describe), EventString),
			    EVENT_TYPE_STRING, event_object_unref);
	event_list_add_data(list, EPTR((euint64)e_item->is_important),
			    EVENT_TYPE_UINT8, NULL);
	raw = event_list_export_raw(list);
	event_object_unref(list);
	return event_file_driver_pack_new_from_raw(raw);
}

static int
event_table_prepare_data(EventFileDriver *driver)
{
	EventTable *self = EVENT_TABLE(driver);
	return_val_if_fail(driver != NULL, event_table_prepare_data,
			   EVENT_FILE_DRIVER_PREPARE_DATA_ERROR);
	event_hash_iter_reset(EVENT_TABLE(driver)->e_table);
	event_file_driver_flush_items_num(
		driver, (eint32)event_hash_get_used_size(self->e_table));
	return EVENT_FILE_DRIVER_SUCCESS;
}

static void
event_table_finalize(EventTable *self)
{
	event_file_driver_finalize(EVENT_FILE_DRIVER(self));
	event_object_unref(self->e_table);
}

EventTable *
event_table_new(void)
{
	EventTable *object = MALLOC_NEW(EventTable);

	event_file_driver_init(EVENT_FILE_DRIVER(object));
	event_object_set_finalize(object, FINALIZE(event_table_finalize));

	event_file_driver_set_parser(EVENT_FILE_DRIVER(object),
				     event_table_parser);
	event_file_driver_set_writer(EVENT_FILE_DRIVER(object),
				     event_table_writer,
				     event_table_prepare_data);
	object->e_table = event_hash_new(EVENT_HASH_MODEL_NUMBER);
	// object->group_id = 0;

	return object;
}

static euint32
random_event_id(euint32 user_id, time_t now_time)
{
	static euint32 already = 0;
	if (user_id == 0) {
		already = 0;
		return 0;
	}
	if (already == 0)
		already = user_id;
	else
		already += (now_time % 8399507);
	return already % 8999509;
}

euint32
event_table_set_event(EventTable *self, euint32 event_id, euint32 user_id,
		      time_t end_time, EventString *event_name,
		      EventString *event_describe, bool is_important)
{
	EventItem *e_item, *tmp;
	time_t	   now_time;
	return_val_if_fail(self != NULL, event_table_set_event, 0);

	now_time = time(NULL);
	if (event_id == 0) {
		e_item		    = event_item_new();
		e_item->create_time = now_time;
		do {
			e_item->event_id =
				random_event_id(user_id, now_time) + 1000000;
			tmp = (EventItem *)event_hash_get(
				self->e_table, EPTR((euint64)e_item->event_id));
		} while (tmp);
		random_event_id(0, 0);
		event_hash_set(self->e_table, EPTR((euint64)e_item->event_id),
			       e_item, event_object_unref);
	} else {
		e_item = (EventItem *)event_hash_get(self->e_table,
						     EPTR((euint64)event_id));
		if (!e_item)
			return 0;
	}
	e_item->modify_time  = now_time;
	e_item->create_uid   = user_id;
	e_item->end_time     = end_time;
	e_item->is_important = is_important;
	event_object_unref(e_item->event_name);
	event_object_unref(e_item->event_describe);
	e_item->event_name     = EO_REF(event_name, EventString);
	e_item->event_describe = EO_REF(event_describe, EventString);
	return e_item->event_id;
}

int
event_table_remove_event(EventTable *self, euint32 event_id)
{
	return_val_if_fail(self != NULL, event_table_remove_event, 1);

	if (!event_hash_get(self->e_table, EPTR((euint64)event_id)))
		return 2;
	event_hash_remove(self->e_table, EPTR((euint64)event_id));
	return 0;
}

void
event_table_iter_reset(EventTable *self)
{
	return_if_fail(self != NULL, event_table_iter_reset);
	event_hash_iter_reset(self->e_table);
}

EventItem *
event_table_iter_get(EventTable *self)
{
	HashItem_p h_item;
	return_val_if_fail(self != NULL, event_table_iter_get, NULL);
	h_item = event_hash_iter_get(self->e_table);
	if (h_item)
		return (EventItem *)event_hash_item_get_value(h_item);
	else
		return NULL;
}

void
__event_table_print(EventTable *self)
{
	EventItem *e_item;
	HashItem_p h_item;
	return_if_fail(self, __event_table_print);

	printf(_("Event table\n"));
	printf(_("EventID\tUserID\tcreatDate\tmodifyDate\tendData\teventName\te"
		 "ventDescribe\n"));
	event_hash_iter_reset(self->e_table);
	h_item = event_hash_iter_get(self->e_table);
	while (h_item) {
		e_item = (EventItem *)event_hash_item_get_value(h_item);
		printf("%u\t%u\t%ld\t%ld\t%ld\t%s\t%s\n", e_item->event_id,
		       e_item->create_uid, e_item->create_time,
		       e_item->modify_time, e_item->end_time,
		       ES_GET(e_item->event_name),
		       ES_GET(e_item->event_describe));
		h_item = event_hash_iter_get(self->e_table);
	}
}
